use std::ops::Sub;
use std::sync::atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering};
use std::sync::{Arc, Mutex};
use std::thread::JoinHandle;
use std::time::Instant;
use std::{hint, thread};

use crate::gen_title;

pub fn ch4_15_1() {
    gen_title("ch4_15_1", 18);

    const N_TIMES: u64 = 1000000;
    const N_THREADS: usize = 10;

    static R: AtomicU64 = AtomicU64::new(0);

    fn add_n_times(n: u64) -> JoinHandle<()> {
        thread::spawn(move || {
            for _ in 0..n {
                R.fetch_add(1, Ordering::Relaxed);
            }
        })
    }

    let s = Instant::now();
    let mut threads = Vec::with_capacity(N_THREADS);

    for _ in 0..N_THREADS {
        threads.push(add_n_times(N_TIMES));
    }

    for thread in threads {
        thread.join().unwrap();
    }

    assert_eq!(N_TIMES * N_THREADS as u64, R.load(Ordering::Relaxed));

    println!("{:?}", Instant::now().sub(s));
}

pub fn ch4_15_2() {
    gen_title("ch4_15_2", 18);

    struct Counter {
        count: u64,
    }

    let n = Mutex::new(Counter { count: 0 });

    n.lock().unwrap().count += 1;

    println!("{}", n.lock().unwrap().count);

    let n = AtomicU64::new(0);
    n.fetch_add(0, Ordering::Relaxed);

    println!("{}", n.load(Ordering::Relaxed));
}

pub fn ch4_15_3() {
    gen_title("ch4_15_3", 18);

    static mut DATA: u64 = 0;
    static READY: AtomicBool = AtomicBool::new(false);

    fn reset() {
        unsafe {
            DATA = 0;
        }
        READY.store(false, Ordering::Relaxed);
    }

    fn producer() -> JoinHandle<()> {
        thread::spawn(move || {
            unsafe {
                DATA = 100; // A
            }
            READY.store(true, Ordering::Release);
            println!("producer DATA = {}", unsafe { DATA }) // B: 内存屏障 ↑
        })
    }

    fn consumer() -> JoinHandle<()> {
        thread::spawn(move || {
            while !READY.load(Ordering::Acquire) {} // C: 内存屏障 ↓
            println!("consumer DATA = {}", unsafe { DATA });
            assert_eq!(100, unsafe { DATA }); // D
        })
    }

    for _ in 0..100 {
        reset();

        let t_producer = producer();
        let t_consumer = consumer();

        t_producer.join().unwrap();
        t_consumer.join().unwrap();
    }
}

pub fn ch4_15_4() {
    gen_title("ch4_15_4", 18);

    let spinlock = Arc::new(AtomicUsize::new(1));

    let spinlock_clone = Arc::clone(&spinlock);
    let thread = thread::spawn(move || {
        spinlock_clone.store(0, Ordering::SeqCst);
    });

    while spinlock.load(Ordering::SeqCst) != 0 {
        hint::spin_loop();
        println!("Spinning...");
    }

    if let Err(panic) = thread.join() {
        println!("Thread had an error: {:?}", panic);
    }
}
